// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using NUnit.Framework;
using ValidationAspects.Exceptions;
using ValidationAspects.Factories;

namespace TestValidationAspects.Factories
{
	[TestFixture]
	public class TestInRange
	{
		private static void Fail<T>(double min, double max, T actual)
		{
			Test.Throws<ValidateInRangeException>(() => new InRange {Minimum = min, Maximum = max}.Create<T>().Invoke(actual, new MockValidationContext()),
			                                      new ValidateInRangeException(min, max, actual).Message);
		}

		private static void Pass<T>(double min, double max, T actual)
		{
			new InRange {Minimum = min, Maximum = max}.Create<T>().Invoke(actual, new MockValidationContext());
		}

		[Test]
		[ExpectedException(typeof(UnsupportedValidationException))]
		public void Test_CreateValidator_throws_for_unsupported_type()
		{
			new InRange {Minimum = 0, Maximum = 1}.Create<string>();
		}

		[Test]
		public void Test_CreateValidator_throws_when_min_is_not_less_than_max()
		{
			Test.Throws<ArgumentOutOfRangeException>(() => new InRange {Minimum = 0, Maximum = 0}.Create<double>(),
			                                         new ArgumentOutOfRangeException("Minimum", string.Format("Range minimum must be less than maximum [value:{0}].", 0)).Message);
			Test.Throws<ArgumentOutOfRangeException>(() => new InRange {Minimum = 1, Maximum = 0}.Create<double>(),
			                                         new ArgumentOutOfRangeException("Minimum", string.Format("Range minimum must be less than maximum [value:{0}].", 0)).Message);
		}

		[Test]
		public void Test_Range_double()
		{
			Fail<double>(1, 3, 0);
			Pass<double>(1, 3, 1);
			Pass<double>(1, 3, 2);
			Pass<double>(1, 3, 3);
			Fail<double>(1, 3, 4);
		}

		[Test]
		public void Test_Range_decimal()
		{
			Fail<decimal>(1, 3, 0);
			Pass<decimal>(1, 3, 1);
			Pass<decimal>(1, 3, 2);
			Pass<decimal>(1, 3, 3);
			Fail<decimal>(1, 3, 4);
		}

		[Test]
		public void Test_Range_int()
		{
			Fail(1, 3, 0);
			Pass(1, 3, 1);
			Pass(1, 3, 2);
			Pass(1, 3, 3);
			Fail(1, 3, 4);
		}

		[Test]
		public void Test_Range_long()
		{
			Fail<long>(1, 3, 0);
			Pass<long>(1, 3, 1);
			Pass<long>(1, 3, 2);
			Pass<long>(1, 3, 3);
			Fail<long>(1, 3, 4);
		}

		[Test]
		public void Test_Range_float()
		{
			Fail<float>(1, 3, 0);
			Pass<float>(1, 3, 1);
			Pass<float>(1, 3, 2);
			Pass<float>(1, 3, 3);
			Fail<float>(1, 3, 4);
		}

		[Test]
		public void Test_Range_short()
		{
			Fail<short>(1, 3, 0);
			Pass<short>(1, 3, 1);
			Pass<short>(1, 3, 2);
			Pass<short>(1, 3, 3);
			Fail<short>(1, 3, 4);
		}

		[Test]
		public void Test_Range_char()
		{
			Fail('1', '3', '0');
			Pass('1', '3', '1');
			Pass('1', '3', '2');
			Pass('1', '3', '3');
			Fail('1', '3', '4');
		}

		[Test]
		public void Test_Range_byte()
		{
			Fail<byte>(1, 3, 0);
			Pass<byte>(1, 3, 1);
			Pass<byte>(1, 3, 2);
			Pass<byte>(1, 3, 3);
			Fail<byte>(1, 3, 4);
		}

		[Test]
		public void Test_Range_uint()
		{
			Fail<uint>(1, 3, 0);
			Pass<uint>(1, 3, 1);
			Pass<uint>(1, 3, 2);
			Pass<uint>(1, 3, 3);
			Fail<uint>(1, 3, 4);
		}

		[Test]
		public void Test_Range_ulong()
		{
			Fail<ulong>(1, 3, 0);
			Pass<ulong>(1, 3, 1);
			Pass<ulong>(1, 3, 2);
			Pass<ulong>(1, 3, 3);
			Fail<ulong>(1, 3, 4);
		}

		[Test]
		public void Test_Range_ushort()
		{
			Fail<ushort>(1, 3, 0);
			Pass<ushort>(1, 3, 1);
			Pass<ushort>(1, 3, 2);
			Pass<ushort>(1, 3, 3);
			Fail<ushort>(1, 3, 4);
		}

		[Test]
		public void Test_Range_sbyte()
		{
			Fail<sbyte>(1, 3, 0);
			Pass<sbyte>(1, 3, 1);
			Pass<sbyte>(1, 3, 2);
			Pass<sbyte>(1, 3, 3);
			Fail<sbyte>(1, 3, 4);
		}


		[Test]
		public void Test_Range_Nullable_double()
		{
			Fail<double?>(1, 3, 0);
			Pass<double?>(1, 3, 1);
			Pass<double?>(1, 3, 2);
			Pass<double?>(1, 3, 3);
			Fail<double?>(1, 3, 4);

			Pass<double?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_decimal()
		{
			Fail<decimal?>(1, 3, 0);
			Pass<decimal?>(1, 3, 1);
			Pass<decimal?>(1, 3, 2);
			Pass<decimal?>(1, 3, 3);
			Fail<decimal?>(1, 3, 4);

			Pass<decimal?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_int()
		{
			Fail<int?>(1, 3, 0);
			Pass<int?>(1, 3, 1);
			Pass<int?>(1, 3, 2);
			Pass<int?>(1, 3, 3);
			Fail<int?>(1, 3, 4);

			Pass<int?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_long()
		{
			Fail<long?>(1, 3, 0);
			Pass<long?>(1, 3, 1);
			Pass<long?>(1, 3, 2);
			Pass<long?>(1, 3, 3);
			Fail<long?>(1, 3, 4);

			Pass<long?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_float()
		{
			Fail<float?>(1, 3, 0);
			Pass<float?>(1, 3, 1);
			Pass<float?>(1, 3, 2);
			Pass<float?>(1, 3, 3);
			Fail<float?>(1, 3, 4);

			Pass<float?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_short()
		{
			Fail<short?>(1, 3, 0);
			Pass<short?>(1, 3, 1);
			Pass<short?>(1, 3, 2);
			Pass<short?>(1, 3, 3);
			Fail<short?>(1, 3, 4);

			Pass<short?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_char()
		{
			Fail<char?>('1', '3', '0');
			Pass<char?>('1', '3', '1');
			Pass<char?>('1', '3', '2');
			Pass<char?>('1', '3', '3');
			Fail<char?>('1', '3', '4');

			Pass<char?>('1', '3', null);
		}

		[Test]
		public void Test_Range_Nullable_byte()
		{
			Fail<byte?>(1, 3, 0);
			Pass<byte?>(1, 3, 1);
			Pass<byte?>(1, 3, 2);
			Pass<byte?>(1, 3, 3);
			Fail<byte?>(1, 3, 4);

			Pass<byte?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_uint()
		{
			Fail<uint?>(1, 3, 0);
			Pass<uint?>(1, 3, 1);
			Pass<uint?>(1, 3, 2);
			Pass<uint?>(1, 3, 3);
			Fail<uint?>(1, 3, 4);

			Pass<uint?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_ulong()
		{
			Fail<ulong?>(1, 3, 0);
			Pass<ulong?>(1, 3, 1);
			Pass<ulong?>(1, 3, 2);
			Pass<ulong?>(1, 3, 3);
			Fail<ulong?>(1, 3, 4);

			Pass<ulong?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_ushort()
		{
			Fail<ushort?>(1, 3, 0);
			Pass<ushort?>(1, 3, 1);
			Pass<ushort?>(1, 3, 2);
			Pass<ushort?>(1, 3, 3);
			Fail<ushort?>(1, 3, 4);

			Pass<ushort?>(1, 3, null);
		}

		[Test]
		public void Test_Range_Nullable_sbyte()
		{
			Fail<sbyte?>(1, 3, 0);
			Pass<sbyte?>(1, 3, 1);
			Pass<sbyte?>(1, 3, 2);
			Pass<sbyte?>(1, 3, 3);
			Fail<sbyte?>(1, 3, 4);

			Pass<sbyte?>(1, 3, null);
		}
	}
}